package com.gitee.wsl.mathematics.geometry.d2.contour.ext

import com.gitee.wsl.mathematics.geometry.api.ShapeTopology
import com.gitee.wsl.mathematics.geometry.d2.contour.ContourPoint
import com.gitee.wsl.mathematics.geometry.d2.contour.ContourShape
import com.gitee.wsl.mathematics.geometry.d2.contour.ShapeContour
import com.gitee.wsl.mathematics.geometry.d2.contour.contains
import com.gitee.wsl.mathematics.geometry.d2.contour.region.toRegion2
import com.gitee.wsl.mathematics.geometry.d2.contour.region.toShape
import com.gitee.wsl.mathematics.geometry.d2.contour.region.toShapes
import com.gitee.wsl.mathematics.geometry.d2.contour.segment.SegmentPoint
import com.gitee.wsl.mathematics.vector.vec2.Vec2f
import kotlin.jvm.JvmName
import kotlin.math.abs


/** Applies a boolean intersection operation between the [ShapeContour] and a [ContourShape]. */
@Suppress("unused")
fun ShapeContour.intersection(other: ContourShape) = intersection(this, other)

/**
 * Applies a boolean intersection operation between two [ShapeContour]s.
 */
@JvmName("intersectionShapeContour")
fun intersection(from: ShapeContour, with: ShapeContour): ContourShape {
    if (from.empty || with.empty)
        return ContourShape.EMPTY

    return if (from.closed) {
        val result = from.ring2.region().intersection(with.ring2.region())
        result.toShape()
    } else {
        return if (with.closed) {
            val ints = intersections(from, with)
            return if (ints.isNotEmpty()) {
                val sortedInts = ints.map { it.a.contourT }.sorted()
                val weldedInts = (listOf(if (sortedInts.first() > 0.0) 0.0 else null) + sortedInts + (if (sortedInts.last() < 1.0) 1.0 else null)).filterNotNull().merge { a, b ->
                    abs(a - b) < 1E-6
                }
                val partitions = weldedInts.zipWithNext().mapNotNull {
                    val partition = from.sub(it.first, it.second)
                    if (partition.position(0.5f) in with) {
                        partition
                    } else {
                        null
                    }
                }
                ContourShape(partitions)
            } else {
                if (from.position(0.0f) in with) from.shape else ContourShape.EMPTY
            }
        } else {
            from.shape
        }
    }
}

/**
 * Applies a boolean intersection operation between a [ContourShape] and a [ShapeContour].
 */
fun intersection(from: ContourShape, with: ShapeContour): ContourShape {
    if (from.empty || with.empty) {
        return ContourShape.EMPTY
    }

    return when (from.topology) {
        ShapeTopology.CLOSED -> {
            if (with.closed) {
                val result = from.region2.intersection(with.ring2.region())
                result.toShape()
            } else {
                return from
            }
        }
        ShapeTopology.OPEN -> {
            if (with.closed) {
                ContourShape.compound(from.contours.map {
                    intersection(it, with)
                })
            } else {
                return from
            }
        }
        ShapeTopology.MIXED -> {
            if (with.closed) {
                ContourShape.compound(from.splitCompounds().map {
                    intersection(it, with)
                })
            } else {
                return from
            }
        }
    }
}

/**
 * Applies a boolean intersection operation between a [ShapeContour] and [ContourShape].
 */
@JvmName("intersectionContourShape")
fun intersection(from: ShapeContour, with: ContourShape): ContourShape {
    if (from.empty || with.empty) {
        return ContourShape.EMPTY
    }

    return if (from.closed) {
        val result = from.ring2.region().intersection(with.region2)
        result.toShape()
    } else {
        when (with.topology) {
            ShapeTopology.CLOSED -> {
                val ints = with.contours.flatMap { intersections(from, it) }
                if (ints.isNotEmpty()) {
                    val sortedInts = ints.map { it.a.contourT }.sorted()
                    val weldedInts = (listOf(if (sortedInts.first() > 0.0) 0.0 else null) + sortedInts + (if (sortedInts.last() < 1.0) 1.0 else null)).filterNotNull().merge { a, b ->
                        abs(a - b) < 1E-6
                    }
                    val partitions = weldedInts.zipWithNext().mapNotNull {
                        val partition = from.sub(it.first, it.second)
                        if (partition.position(0.5f) in with) {
                            partition
                        } else {
                            null
                        }
                    }
                    ContourShape(partitions)

                } else {
                    if (from.position(0.0f) in with) from.shape else ContourShape.EMPTY
                }
            }
            ShapeTopology.OPEN -> {
                from.shape
            }
            ShapeTopology.MIXED -> {
                return intersection(
                    from,
                    ContourShape(with.splitCompounds().filter { it.topology == ShapeTopology.OPEN }.flatMap { it.contours })
                )
            }
        }
    }
}

/**
 * Applies a boolean intersection operation between two [ContourShape]s.
 */
fun intersection(from: ContourShape, with: ContourShape): ContourShape {
    return when (from.topology) {
        ShapeTopology.OPEN -> {
            when (with.topology) {
                ShapeTopology.OPEN -> from
                ShapeTopology.CLOSED -> {
                    ContourShape.compound(from.contours.map { intersection(it, with) })
                }
                ShapeTopology.MIXED -> {
                    val closed = ContourShape(from.splitCompounds().filter { it.topology == ShapeTopology.CLOSED }
                        .flatMap { it.contours })
                    intersection(closed, with)
                }
            }
        }
        ShapeTopology.CLOSED -> {
            when (with.topology) {
                ShapeTopology.OPEN -> from
                ShapeTopology.CLOSED -> {
                    val result = from.region2.intersection(with.region2)
                    result.toShape()
                }
                ShapeTopology.MIXED -> {
                    val closed = ContourShape(from.splitCompounds().filter { it.topology == ShapeTopology.CLOSED }
                        .flatMap { it.contours })
                    intersection(closed, with)
                }
            }
        }
        ShapeTopology.MIXED -> {
            val closed =
                ContourShape(from.splitCompounds().filter { it.topology == ShapeTopology.CLOSED }.flatMap { it.contours })
            val open = from.openContours
            ContourShape.compound(listOf(intersection(closed, with)) + open.map { intersection(it, with) })
        }
    }
}

/**
 * Applies a boolean intersection operation between a [List] of [ContourShape]s and a [ShapeContour].
 */
fun intersection(from: List<ContourShape>, with: ShapeContour): List<ContourShape> {
    return from.toRegion2().intersection(with.ring2.region()).toShapes()
}

/**
 * Applies a boolean intersection operation between a [List] of [ContourShape]s and a [ContourShape].
 */
fun intersection(from: List<ContourShape>, with: ContourShape): List<ContourShape> {
    return from.toRegion2().intersection(with.region2).toShapes()
}

/**
 * Applies a boolean intersection operation between two [List]s of [ContourShape]s.
 */
fun intersection(from: List<ContourShape>, with: List<ContourShape>): List<ContourShape> {
    return from.toRegion2().intersection(with.toRegion2()).toShapes()
}

/**
 * Applies a boolean intersection operation iteratively between a [List] of [ContourShape]s and a two-dimensional [List] of [ContourShape]s.
 *
 * [with] is traversed and a boolean [intersection] is applied between [from] and each element.
 */
@JvmName("intersectionIterative")
fun intersection(from: List<ContourShape>, with: List<List<ContourShape>>): List<ContourShape> {
    var left = from
    for (withShapes in with) {
        left = intersection(left, withShapes)
    }
    return left
}

data class SegmentIntersection(val a: SegmentPoint, val b: SegmentPoint, val position: Vec2f)

data class ContourIntersection(val a: ContourPoint, val b: ContourPoint, val position: Vec2f)


